package org.zjx.analysis.identify;


import org.zjx.command.entity.Rect;
import org.zjx.command.entity.WordBean;

import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.*;
import java.util.List;
import java.util.Queue;

public class LiterateUtil {
    // 边界定义
    private static int[] dx = {1, -1, 0, 0, 1, 1, -1, -1,-1,-1,0,0,1,1};  // 横向和对角线移动
    private static int[] dy = {0, 0, 1, -1, 1, -1, 1, -1,2,-2,2,-2,2,-2};  // 纵向和对角线移动

    //打印点阵
    public static void printBlock(int[][] block) {
        for (int[] ints : block) {
            for (int anInt : ints) {
                // 打印 0 为空白，非0的点打印为星号 (*) 或其他符号
                if (anInt == 0) {
                    System.out.print(" ");  // 空白
                } else {
                    System.out.print("1");  // 非0的点打印为星号
                }
            }
            System.out.println();  // 换行
        }
    }


    /**
     * 或取指定颜色的字体
     * @param image
     * @param colors
     * @return
     */
    public static List<List<WordBean>> identify(BufferedImage image, int wordHeight, List<Color> colors,boolean reverse) {
        int[][] blocks = conversion(image, colors,reverse);
        return extractBlocks(blocks, wordHeight);
    }


    public static WordBean mergeWord(WordBean wordBean1, WordBean wordBean2) {
        // 获取两个 wordBean 的 rect 和 block
        Rect rect1 = wordBean1.getRect();
        Rect rect2 = wordBean2.getRect();
        int[][] block1 = wordBean1.getBlock();
        int[][] block2 = wordBean2.getBlock();

        // 合并 rect 的区域，计算并集
        // 创建合并后的矩形区域
        Rect mergedRect = new Rect(Math.min(rect1.getLeft(), rect2.getLeft()),Math.min(rect1.getTop(), rect2.getTop()),Math.max(rect1.getRight(), rect2.getRight()),Math.max(rect1.getBottom(), rect2.getBottom()));

        // 计算合并后 block 的大小
        int width = mergedRect.getRight() - mergedRect.getLeft() + 1;
        int height = mergedRect.getBottom() - mergedRect.getTop() + 1;

        // 初始化合并后的 block
        int[][] mergedBlock = new int[height][width];

        // 合并两个 block
        mergeBlocks(block1, rect1, mergedBlock, mergedRect);
        mergeBlocks(block2, rect2, mergedBlock, mergedRect);

        // 创建新的 WordBean，设置合并后的 rect 和 block
        return new WordBean(mergedRect, mergedBlock);
    }

    // 合并单个 block 到合并后的 block 中
    private static void mergeBlocks(int[][] block, Rect rect, int[][] mergedBlock, Rect mergedRect) {
        for (int i = 0; i < block.length; i++) {
            for (int j = 0; j < block[i].length; j++) {
                // 计算在合并后的 block 中的对应位置
                int newX = rect.getLeft() + j - mergedRect.getLeft();
                int newY = rect.getTop() + i - mergedRect.getTop();
                mergedBlock[newY][newX] = block[i][j];
            }
        }
    }


    // 提取块的函数
    private static List<List<WordBean>> extractBlocks(int[][] matrix, int fontHeight) {
        int rows = matrix.length;
        int cols = matrix[0].length;
        boolean[][] visited = new boolean[rows][cols];
        List<List<WordBean>> blocks = new ArrayList<>();

        int before[] = {0};
        int height = getHeight(matrix, rows, cols, visited, fontHeight,before);
        while (height > 0) {
            List<WordBean> interior = new ArrayList<>();
            // 遍历整个矩阵，执行广度优先搜索
            for (int i = 0; i < height + 1; i++) {
                for (int j = 0; j < cols; j++) {
                    if (matrix[i][j] == 1 && !visited[i][j]) {
                        // 找到一个未访问过的块，执行 BFS
                        WordBean block = bfs(matrix, visited, j, i, rows, cols, height);
                        if (height - before[0] + 1 < fontHeight && before[0] + fontHeight > rows) {
                            continue;
                        }
                        interior.add(block);
                    }
                }
            }
//            for (WordBean wordBean : interior) {
//                printBlock(wordBean.getBlock());
//            }
            interior = mergeHorizontalWords(matrix,interior);
//            for (WordBean wordBean : interior) {
//                printBlock(wordBean.getBlock());
//            }


            blocks.add(interior);
            height = getHeight(matrix, rows, cols, visited, fontHeight,before);
        }
        return blocks;
    }

    // 合并行内的字体
    private static List<WordBean> mergeHorizontalWords(int[][] matrix,List<WordBean> wordBeans) {
        // 对每一行内的 WordBean 进行排序，按 left 值排序
        wordBeans.sort(Comparator.comparingInt(WordBean::getLeft));

        List<WordBean> mergedWords = new ArrayList<>();

        // 如果没有 WordBean，直接返回空列表
        if (wordBeans.isEmpty()) {
            return mergedWords;
        }

        // 使用一个临时列表来保存当前待合并的字体
        List<WordBean> currentWords = new ArrayList<>();
        currentWords.add(wordBeans.get(0));  // 第一个 WordBean 加入待合并列表

        // 从第二个 WordBean 开始遍历
        for (int i = 1; i < wordBeans.size(); i++) {
            WordBean currentBean = wordBeans.get(i);
            WordBean lastBean = currentWords.get(currentWords.size() - 1);

            // 如果当前 WordBean 和最后一个 WordBean 有重叠（即右边界大于等于左边界）
            if (lastBean.getRight() >= currentBean.getLeft()) {
                // 合并这两个字体
                currentWords.set(currentWords.size() - 1, mergeHorizontally(matrix,lastBean, currentBean));
            } else {
                // 没有重叠，输出当前合并的 WordBean
                mergedWords.add(currentWords.get(0));
                // 重置 currentWords，开始合并下一个
                currentWords.clear();
                currentWords.add(currentBean);
            }
        }

        // 添加最后一个合并的字体
        if (!currentWords.isEmpty()) {
            mergedWords.add(currentWords.get(0));
        }

        return mergedWords;
    }

    // 合并两个 WordBean，返回一个新的 WordBean
    private static WordBean mergeHorizontally(int[][] matrix,WordBean b1, WordBean b2) {
        // 合并两个 WordBean 的矩形区域（合并左右边界）
        int newLeft = Math.min(b1.getLeft(), b2.getLeft());
        int newRight = Math.max(b1.getRight(), b2.getRight());
        int newTop = Math.min(b1.getTop(), b2.getTop());
        int newBottom = Math.max(b1.getBottom(), b2.getBottom());

        Rect rect = new Rect(newLeft,newTop,newRight,newBottom);

        return new WordBean(rect, extractBlockFromMatrix(matrix,newLeft,newTop,newRight,newBottom));
    }


    // 广度优先搜索的主函数
    private static WordBean bfs(int[][] matrix, boolean[][] visited, int startX, int startY, int rows, int cols, int fontHeight) {
        // 初始化队列

        Queue<int[]> queue = new LinkedList<>();
        queue.add(new int[]{startX, startY});
        visited[startY][startX] = true;

        // 初始化边界
        int minX = startX, minY = startY, maxX = startX, maxY = startY;

        // 开始广度优先搜索
        while (!queue.isEmpty()) {
            int[] current = queue.poll();
            int curX = current[0], curY = current[1];

            // 扩展上下左右相邻的点
            for (int i = 0; i < dx.length; i++) {
                int newX = curX + dx[i];
                int newY = curY + dy[i];

                // 判断新位置是否在有效范围内，且是非0点，且没有访问过
                if (newX >= 0 && newX < cols && newY >= 0 && newY < rows && matrix[newY][newX] == 1 && !visited[newY][newX]) {
                    // 只继续扩展未超过最大高度的区域
                    if (newY <= fontHeight) {
                        queue.add(new int[]{newX, newY});
                        visited[newY][newX] = true;

                        // 更新边界
                        minX = Math.min(minX, newX);
                        minY = Math.min(minY, newY);
                        maxX = Math.max(maxX, newX);
                        maxY = Math.max(maxY, newY);


                        // 将新位置加入队列，标记为已访问
                        addUnvisitedToQueue(minX, minY, maxX, maxY, queue, visited);

                    }
                }
            }
        }

        // 提取字块区域的二维点阵信息
        int[][] block = extractBlockFromMatrix(matrix, minX, minY, maxX, maxY);

        // 创建RECT对象，表示矩形的边界
        Rect rect = new Rect(minX, minY, maxX, maxY);

        // 返回一个新的WordBean，包含矩形和块
        return new WordBean(rect, block);
    }

    public boolean havePoint(int[][] matrix,int x , int topY,int fontHeight){
        for (int y = topY; y <= topY + fontHeight; y++) {
            if (matrix[x][y] == 1) return true;
        }
        return false;
    }

    // 添加范围内所有未访问的点到队列中
    private static void addUnvisitedToQueue(int minX, int minY, int maxX, int maxY, Queue<int[]> queue, boolean[][] visited) {
        // 遍历给定范围内的所有点
        for (int x = minX; x <= maxX; x++) {
            for (int y = minY; y <= maxY; y++) {
                // 如果该点未访问过
                if (!visited[y][x]) {
                    // 将该点添加到队列
                    queue.add(new int[]{x, y});
                    // 标记为已访问
                    visited[y][x] = true;
                }
            }
        }
    }


    // 提取字块区域的二维点阵信息
    private static int[][] extractBlockFromMatrix(int[][] matrix, int minX, int minY, int maxX, int maxY) {
        int[][] block = new int[maxY - minY + 1][maxX - minX + 1];
        for (int i = minY; i <= maxY; i++) {
            if (maxX + 1 - minX >= 0) System.arraycopy(matrix[i], minX, block[i - minY], 0, maxX + 1 - minX);
        }
        return block;
    }

    private static int getHeight(int[][] matrix, int rows, int cols, boolean[][] visited, int height,int[] before) {
        int top = -1;  // 顶部行号，初始化为 -1 表示还未找到
        int bottom = -1;  // 底部行号，初始化为 -1 表示还未找到

        // 遍历每一行，找到顶部（第一个为1且未访问过的位置）
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                if (matrix[i][j] == 1 && !visited[i][j]) {  // 判断该位置为1且没有访问过
                    top = i;  // 找到顶部
                    break;  // 找到顶部就停止遍历当前行
                }
            }
            if (top != -1) break;  // 如果找到顶部，停止遍历行
        }

        // 如果未找到顶部，返回-1或适当值
        if (top == -1) {
            return -1;  // 或者根据你的逻辑返回其他值
        }else {
            before[0] = top;
        }

        // 从顶部开始向下查找，找到底部
        for (int i = top + 1; i < rows; i++) {
            boolean allZero = true;  // 假设当前行是全0的

            for (int j = 0; j < cols; j++) {
                if (matrix[i][j] == 1 && !visited[i][j]) {  // 如果是1并且没有访问过
                    allZero = false;
                    break;
                }
            }

            if (allZero) {
                bottom = i - 1;  // 上一行就是底部
                break;
            }
        }

        // 如果没有找到底部，底部为最后一行
        if (bottom == -1) {
            bottom = rows - 1;
        }

        // 计算最终的高度
        int finalHeight = top + height - 1;

        // 如果最终的高度大于底部的行，则限制为底部行的高度
        if (finalHeight > bottom || finalHeight < top) {
            finalHeight = bottom;
        }

        return finalHeight;
    }


    //讲指定颜色转1，背景转0，输出二维点阵
    private static int[][] conversion(BufferedImage image, List<Color> colors,boolean reverse) {
        int width = image.getWidth();
        int height = image.getHeight();

        // 创建一个二维数组来存储转换后的像素值
        int[][] result = new int[height][width];

        // 遍历每个像素
        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width; x++) {
                Color pixelColor = new Color(image.getRGB(x, y));

                // 检查当前像素颜色是否在给定的颜色列表中
                boolean isMatchingColor = false;
                for (Color color : colors) {
                    if (isSameColor(pixelColor, color)) {
                        isMatchingColor = true;
                        break;
                    }
                }

                // 如果匹配颜色，设为1，否则设为0
                if (isMatchingColor) {
                    result[y][x] = reverse?0:1;
                } else {
                    result[y][x] = reverse?1:0;
                }
            }
        }

        return result;
    }


    // 判断两个颜色是否相同
    private static boolean isSameColor(Color c1, Color c2) {
        return c1.getRed() == c2.getRed() && c1.getGreen() == c2.getGreen() && c1.getBlue() == c2.getBlue();
    }
}
